/**
 * 
 */
package jvs.vfs.opt;

import java.net.URISyntaxException;

import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;

/**
 * @author qiangli
 * 
 */
public final class Intents {

	private Intents() {
	}

	public static final String ACTION_VFS_MOUNT_COMPLETED = "jvs.vfs.opt.action.vfs_mount_completed";

	/**
	 * Flag for use with {@link #toUri} and {@link #parseUri}: the URI string
	 * always has the "intent:" scheme. This syntax can be used when you want to
	 * later disambiguate between URIs that are intended to describe an Intent
	 * vs. all others that should be treated as raw URIs. When used with
	 * {@link #parseUri}, any other scheme will result in a generic VIEW action
	 * for that raw URI.
	 */
	public static final int URI_INTENT_SCHEME = 1 << 0;

	public static final String ACTION_VIEW = "android.intent.action.VIEW";

	public static Intent getIntent(String uri) throws URISyntaxException {
		return parseUri(uri, 0);
	}

	//
	/**
	 * Create an intent from a URI. This URI may encode the action, category,
	 * and other intent fields, if it was returned by {@link #toUri}. If the
	 * Intent was not generate by toUri(), its data will be the entire URI and
	 * its action will be ACTION_VIEW.
	 * 
	 * <p>
	 * The URI given here must not be relative -- that is, it must include the
	 * scheme and full path.
	 * 
	 * @param uri
	 *            The URI to turn into an Intent.
	 * @param flags
	 *            Additional processing flags. Either 0 or
	 *            {@link #URI_INTENT_SCHEME}.
	 * 
	 * @return Intent The newly created Intent object.
	 * 
	 * @throws URISyntaxException
	 *             Throws URISyntaxError if the basic URI syntax it bad (as
	 *             parsed by the Uri class) or the Intent data within the URI is
	 *             invalid.
	 * 
	 * @see #toUri
	 */
	public static Intent parseUri(String uri, int flags)
			throws URISyntaxException {
		int i = 0;
		try {
			// Validate intent scheme for if requested.
			if ((flags & URI_INTENT_SCHEME) != 0) {
				if (!uri.startsWith("intent:")) {
					Intent intent = new Intent(ACTION_VIEW);
					try {
						intent.setData(Uri.parse(uri));
					} catch (IllegalArgumentException e) {
						throw new URISyntaxException(uri, e.getMessage());
					}
					return intent;
				}
			}

			// simple case
			i = uri.lastIndexOf("#");
			if (i == -1)
				return new Intent(ACTION_VIEW, Uri.parse(uri));

			// old format Intent URI
			if (!uri.startsWith("#Intent;", i))
				return getIntentOld(uri);

			// new format
			Intent intent = new Intent(ACTION_VIEW);

			// fetch data part, if present
			String data = i >= 0 ? uri.substring(0, i) : null;
			String scheme = null;
			i += "#Intent;".length();

			// loop over contents of Intent, all name=value;
			while (!uri.startsWith("end", i)) {
				int eq = uri.indexOf('=', i);
				int semi = uri.indexOf(';', eq);
				String value = Uri.decode(uri.substring(eq + 1, semi));

				// action
				if (uri.startsWith("action=", i)) {
					String action = value;
					intent.setAction(action);
				}

				// categories
				else if (uri.startsWith("category=", i)) {
					intent.addCategory(value);
				}

				// type
				else if (uri.startsWith("type=", i)) {
					String type = value;
					intent.setType(type);
				}

				// launch flags
				else if (uri.startsWith("launchFlags=", i)) {
					intent.setFlags(Integer.decode(value).intValue());
				}

				// package
				else if (uri.startsWith("package=", i)) {
					//intent.mPackage = value;
					//TODO
					intent.setClassName(value, null);
				}

				// component
				else if (uri.startsWith("component=", i)) {
					ComponentName component = ComponentName
							.unflattenFromString(value);
					intent.setComponent(component);
				}

				// scheme
				else if (uri.startsWith("scheme=", i)) {
					scheme = value;
				}

				// source bounds
				else if (uri.startsWith("sourceBounds=", i)) {
					//intent.mSourceBounds = Rect.unflattenFromString(value);
					//
				}

				// extra
				else {
					String key = Uri.decode(uri.substring(i + 2, eq));
					// create Bundle if it doesn't already exist
					Bundle extras = intent.getExtras();
					if (extras == null) {
						extras = new Bundle();
					}
					Bundle b = extras;
					// add EXTRA
					if (uri.startsWith("S.", i))
						b.putString(key, value);
					else if (uri.startsWith("B.", i))
						b.putBoolean(key, Boolean.parseBoolean(value));
					else if (uri.startsWith("b.", i))
						b.putByte(key, Byte.parseByte(value));
					else if (uri.startsWith("c.", i))
						b.putChar(key, value.charAt(0));
					else if (uri.startsWith("d.", i))
						b.putDouble(key, Double.parseDouble(value));
					else if (uri.startsWith("f.", i))
						b.putFloat(key, Float.parseFloat(value));
					else if (uri.startsWith("i.", i))
						b.putInt(key, Integer.parseInt(value));
					else if (uri.startsWith("l.", i))
						b.putLong(key, Long.parseLong(value));
					else if (uri.startsWith("s.", i))
						b.putShort(key, Short.parseShort(value));
					else
						throw new URISyntaxException(uri, "unknown EXTRA type",
								i);
					intent.putExtras(extras);
				}

				// move to the next item
				i = semi + 1;
			}

			if (data != null) {
				if (data.startsWith("intent:")) {
					data = data.substring(7);
					if (scheme != null) {
						data = scheme + ':' + data;
					}
				}

				if (data.length() > 0) {
					try {
						intent.setData( Uri.parse(data));
					} catch (IllegalArgumentException e) {
						throw new URISyntaxException(uri, e.getMessage());
					}
				}
			}

			return intent;

		} catch (IndexOutOfBoundsException e) {
			throw new URISyntaxException(uri, "illegal Intent URI format", i);
		}
	}

	public static Intent getIntentOld(String uri) throws URISyntaxException {
		Intent intent;

		int i = uri.lastIndexOf('#');
		if (i >= 0) {
			String action = null;
			final int intentFragmentStart = i;
			boolean isIntentFragment = false;

			i++;

			if (uri.regionMatches(i, "action(", 0, 7)) {
				isIntentFragment = true;
				i += 7;
				int j = uri.indexOf(')', i);
				action = uri.substring(i, j);
				i = j + 1;
			}

			intent = new Intent(action);

			if (uri.regionMatches(i, "categories(", 0, 11)) {
				isIntentFragment = true;
				i += 11;
				int j = uri.indexOf(')', i);
				while (i < j) {
					int sep = uri.indexOf('!', i);
					if (sep < 0)
						sep = j;
					if (i < sep) {
						intent.addCategory(uri.substring(i, sep));
					}
					i = sep + 1;
				}
				i = j + 1;
			}

			if (uri.regionMatches(i, "type(", 0, 5)) {
				isIntentFragment = true;
				i += 5;
				int j = uri.indexOf(')', i);
				String type = uri.substring(i, j);
				intent.setType(type);
				i = j + 1;
			}

			if (uri.regionMatches(i, "launchFlags(", 0, 12)) {
				isIntentFragment = true;
				i += 12;
				int j = uri.indexOf(')', i);
				int flags = Integer.decode(uri.substring(i, j)).intValue();
				intent.setFlags(flags);
				i = j + 1;
			}

			if (uri.regionMatches(i, "component(", 0, 10)) {
				isIntentFragment = true;
				i += 10;
				int j = uri.indexOf(')', i);
				int sep = uri.indexOf('!', i);
				if (sep >= 0 && sep < j) {
					String pkg = uri.substring(i, sep);
					String cls = uri.substring(sep + 1, j);
					ComponentName component = new ComponentName(pkg, cls);
					intent.setComponent(component);
				}
				i = j + 1;
			}

			if (uri.regionMatches(i, "extras(", 0, 7)) {
				isIntentFragment = true;
				i += 7;

				final int closeParen = uri.indexOf(')', i);
				if (closeParen == -1)
					throw new URISyntaxException(uri,
							"EXTRA missing trailing ')'", i);

				while (i < closeParen) {
					// fetch the key value
					int j = uri.indexOf('=', i);
					if (j <= i + 1 || i >= closeParen) {
						throw new URISyntaxException(uri, "EXTRA missing '='",
								i);
					}
					char type = uri.charAt(i);
					i++;
					String key = uri.substring(i, j);
					i = j + 1;

					// get type-value
					j = uri.indexOf('!', i);
					if (j == -1 || j >= closeParen)
						j = closeParen;
					if (i >= j)
						throw new URISyntaxException(uri, "EXTRA missing '!'",
								i);
					String value = uri.substring(i, j);
					i = j;

					// create Bundle if it doesn't already exist
					Bundle extras = intent.getExtras();
					if (extras == null) {
						extras = new Bundle();
					}
					// add item to bundle
					try {
						switch (type) {
						case 'S':
							extras.putString(key, Uri.decode(value));
							break;
						case 'B':
							extras.putBoolean(key, Boolean.parseBoolean(value));
							break;
						case 'b':
							extras.putByte(key, Byte.parseByte(value));
							break;
						case 'c':
							extras.putChar(key, Uri.decode(value).charAt(0));
							break;
						case 'd':
							extras.putDouble(key, Double.parseDouble(value));
							break;
						case 'f':
							extras.putFloat(key, Float.parseFloat(value));
							break;
						case 'i':
							extras.putInt(key, Integer.parseInt(value));
							break;
						case 'l':
							extras.putLong(key, Long.parseLong(value));
							break;
						case 's':
							extras.putShort(key, Short.parseShort(value));
							break;
						default:
							throw new URISyntaxException(uri,
									"EXTRA has unknown type", i);
						}
						
						intent.putExtras(extras);
					} catch (NumberFormatException e) {
						throw new URISyntaxException(uri,
								"EXTRA value can't be parsed", i);
					}

					char ch = uri.charAt(i);
					if (ch == ')')
						break;
					if (ch != '!')
						throw new URISyntaxException(uri, "EXTRA missing '!'",
								i);
					i++;
				}
			}

			if (isIntentFragment) {
				Uri data = Uri.parse(uri.substring(0, intentFragmentStart));
				intent.setData(data);
			} else {
				Uri data = Uri.parse(uri);
				intent.setData(data);
			}

			if (intent.getAction() == null) {
				// By default, if no action is specified, then use VIEW.
				intent.setAction(ACTION_VIEW);
			}

		} else {
			intent = new Intent(ACTION_VIEW, Uri.parse(uri));
		}

		return intent;
	}

}
